package de.fhd.pms.ctrl;

import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.apache.log4j.Logger;
import org.hibernate.mapping.Map;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import de.fhd.pms.dao.ActionDao;
import de.fhd.pms.dao.DaoException;
import de.fhd.pms.dao.TourDao;
import de.fhd.pms.dao.UserDao;
import de.fhd.pms.dao.UsersTourDao;
import de.fhd.pms.model.Action;
import de.fhd.pms.model.Tour;
import de.fhd.pms.model.User;
import de.fhd.pms.model.UsersTour;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
/**
 * The controller class for Tour.
 */
@Controller
public class TourController {

	private static Logger log = Logger.getLogger(TourController.class);

	public static boolean isEditable(Integer tour_id, Integer user_id) {
		return true;
	}

	private UserDao userDao = null;

	/**
	 * Set UserDao
	 *  */
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
	/**
	 * Get UserDao
	 * @return {@link} userDao object
 	 *  */
	public UserDao getUserDao() {
		return userDao;
	}

	private TourDao tourDao = null;
	/**
	 * Set TourDao
	 * @param tourDao
	 *  */
	public void setTourDao(TourDao tourDao) {
		this.tourDao = tourDao;
	}

	private UsersTourDao usersTourDao = null;
	/**
	 * Set UsesTourDao
	 * @param userTourDao
	 *  */
	public void setUsersTourDao(UsersTourDao usersTourDao) {
		this.usersTourDao = usersTourDao;
	}
	/**
	 * In case of requesting "/addtour.html" & RequestMethod = POST,
	 * the controller opens the .jsp-view addtour and manages data access
	 * @return addtour
	 *  */
	@RequestMapping(value = "/addtour.html", method = RequestMethod.POST)
	public String addNewTour(@Valid Tour tour, BindingResult result) {
		// get username of currently logged in user
		Authentication auth = SecurityContextHolder.getContext()
				.getAuthentication();
		String username = auth.getName();
		log.info("NAME: " + username);
		// get user object by username
		User user = userDao.findByUsername(username);
		log.info("NAME: " + user.getUsername());
		// check result for errors, if errors are - then no tour is added to the database
		if (result.hasErrors()) {
			log.info("new tour adding failed");
			for (ObjectError error : result.getAllErrors()) {
				log.info(" - error:" + error.toString());
			}
			// user stays on the addtour page
			return "addtour";
		}
		// set for tour owner
		tour.setUser(user);
		log.info("USER: " + user);
		// save new tour to the data base
		tourDao.save(tour);
		// user stays on the addtour page
		return "addtour";
	}

	@InitBinder
	public void binder(WebDataBinder binder) {
		binder.registerCustomEditor(Action.class, new PropertyEditorSupport() {
			public void setAsText(String value) {
				try {

					setValue(tourDao.getActionDao().findByActionId(
							Integer.parseInt(value)));
				} catch (NumberFormatException e) {
					// TODO: handle exception
				}

			}

			public String getAsText() {
				String str = null;
				try {
					log.info("ZZ" + (Action) getValue());
					log.info("Z1" + ((Action) getValue()).getId());
					log.info("Z2" + ((Action) getValue()).getId());
					str = ((Action) getValue()).getId().toString();

				} catch (Exception e) {
					// TODO: handle exception
				}
				return str;
			}

		});

	}
	/**
	 * In case of Requesting "/addtour.html" & RequestMethod = GET,
	 * the controller opens the .jsp-view addtour and manages data access 
	 * @return addtour.
	 *  */
	@RequestMapping(value = "/addtour.html", method = RequestMethod.GET)
	public String showAddTourForm(ModelMap model) {
		// add to the model new tour object, to provide possibility of new tour adding
		model.addAttribute("tour", new Tour());
		model.addAttribute("actionValues", tourDao.getActionDao().findAll());
		// user is on the addtour page
		return "addtour";
	}
	
	/**
	 * In case of Requesting "/tourlist.html" & RequestMethod = GET,
	 * the controller opens the .jsp-view tourlist and manages data access
	 * @return tourlist
	 *  */
	@RequestMapping(value = "/tourlist.html", method = RequestMethod.GET)
	public String showToursForm(ModelMap model) {
		// get list with all tours from the database
		// and add them to the model attributes
		List<Tour> tours = tourDao.findAll();
		model.addAttribute("tourValues", tours);
		model.addAttribute("tour", new Tour());
		// user is on the tourlist page
		return "tourlist";
	}
	
	/**
	 * In case of Requesting "/touroperations.html" & RequestMethod = GET,
	 * the Controller opens the .jsp-view touroperations and manages data access 
	 * @return touroperations
	 *  */
	@RequestMapping(value = "/touroperations.html", method = RequestMethod.GET)
	public String operationsWithTour(Tour tour, ModelMap model) {
		// get username of currently logged in user 
		Authentication auth = SecurityContextHolder.getContext()
				.getAuthentication();
		String username = auth.getName();
		// find corresponding to the username user
		User user = userDao.findByUsername(username);
		log.info("NAME: " + user.getUsername());
		// if tour object is incorrect, then no further actions are available 
		if(tour==null || tour.getId()== null){
			// redirect user to the full tourlist
			return "redirect:tourlist.html";
		}
		
		// checking user to be registered to the tour 
		boolean isRegistered;
		isRegistered = usersTourDao.isUserRegisteredToTour(tour,user);
		System.out.println("User is registered: " + isRegistered);

		// for checking tour for having free seats
		boolean freeSeatsAvailable;
		
		// get tour from the database
		Tour tmpTour = tourDao.findByTourId(tour.getId());
		tour = tmpTour;
		// check tour for correctness
		// if tour is not correct,
		// then redirect user to the full tour list
		if(tour==null || tour.getId()== null){
			return "redirect:tourlist.html";
		}
		
		// tour id
		Integer tour_id = tour.getId();
		System.out.println("Tour id: " + tour_id);

		// getting maximally available places for tour 
		Integer availablePlaces = 0;
		availablePlaces = tour.getMax_member_count();
		log.info("tour.getMax_member_count()" + tour.getMax_member_count());
		
		// find out all amount of previously registered users
		int registeredUsers = 0;
		List<UsersTour> usersTours = usersTourDao.getUsersTourByTour(tour);
		if (!usersTours.equals(null)) {
			registeredUsers = usersTours.size();
		}

		//printing for test purposes
		//System.out.println("available: " + availablePlaces);
		//System.out.println("registered: " + registeredUsers);

		// when maximally available places are more then registered users
		// set free place flag freeSeatsAreAvailable to true,
		// otherwise to false
		if (availablePlaces > registeredUsers) {
			freeSeatsAvailable = true;
			log.info("free seats are available");
		} else {
			freeSeatsAvailable = false;
			log.info("free seats are not available");
		}

		// printing for test purposes
		//if (isRegistered) {
		//	log.info("not client");
		//} else {
		//	log.info("is client");
		//}

		// flag for registration to tour purposes
		int possibleToRegister;

		if (isRegistered) {
			// flag set to value, which indicates that user can be unregistered 
			// from tour
			possibleToRegister = 0;
		} else {
			if (freeSeatsAvailable) {
				// flag set to the value, which indicates, 
				// that user can be registered to the tour
				possibleToRegister = 1;
			} else {
				// flag set to the value, which indicates, 
				// that user can not be registered because of no available 
				// places for this tour
				possibleToRegister = 2;
			}

		}

		// add to model possibleToRegister flag and tour id
		model.addAttribute("possibleToRegister", possibleToRegister);
		model.addAttribute("tour_id", tour_id);

		// user is on the page touroperations
		return "touroperations";
	}
	/**
	 * In case of Requesting "/registertotour.html" & RequestMethod = GET,
	 * the Controller opens the .jsp-view touroperations and manages data access
	 * @return touroperations 
	 *  */
	@RequestMapping(value = "/registertotour.html", method = RequestMethod.GET)
	public String fakeRegisterToTour(Tour tour, BindingResult result,
			ModelMap model) {
		// if tour or tour id is null, then user is redirected to the full list with tours
		if (tour == null || tour.getId()==null) {
			return "redirect:tourlist.html";
		}
		// user is redirected to the touroperations page, 
		// where user can register/unregister/see that there are no
		// places according to the current situation for this tour 
		return "redirect:touroperations.html?id=" + tour.getId();

	}
	/**
	 * In case of Requesting "/unregisterfromtour.html" & RequestMethod = GET,
	 * the Controller opens the .jsp-view touroperations and manages data access
	 * @return touroperations
	 *  */
	@RequestMapping(value = "/unregisterfromtour.html", method = RequestMethod.GET)
	public String fakeUnregisterFromTour(Tour tour, BindingResult result,
			ModelMap model) {
		// if tour object is corrupted redirect user to the full tour list 
		if (tour == null || tour.getId()==null) {
			return "redirect:tourlist.html";
		}
		// else redirect user to the operations with specified tour
		return "redirect:touroperations.html?id=" + tour.getId();

	}
	
	/**
	 * In case of Requesting "/registertotour.html" & RequestMethod = POST,
	 * the Controller opens the .jsp-view touroperations and manages data access
	 * @return touroperations
	 *  */
	@RequestMapping(value = "/registertotour.html", method = RequestMethod.POST)
	public String registerToTour(Tour tour, BindingResult result, ModelMap model) {
		// create new empty UsersTour object
		UsersTour usersTour = new UsersTour();
		// get username of currently logged in user
		Authentication auth = SecurityContextHolder.getContext()
				.getAuthentication();
		String username = auth.getName();
		log.info("NAME: " + username);
		// get user with specified username from the data base
		User user = userDao.findByUsername(username);
		log.info("NAME: " + user.getUsername());
		
		// when in result are errors
		if (result.hasErrors()) {
			log.info("registration to tour failed");
			for (ObjectError error : result.getAllErrors()) {
				log.info(" - error:" + error.toString());
			}
			
			// flag for possibility to register to 0 value,
			// what means that user can unregister from tour
			int possibleToRegister = 0;
			// find tour in the data base and create temporal copy 
			Tour tmpTour = tourDao.findByTourId(tour.getId());
			tour = tmpTour;

			// get tour id from tour object
			Integer tour_id = tour.getId();
			System.out.println("Tour id: " + tour_id);
			
			// initialisation of variable availablePlaces, 
			// which will be used to manage user registration to tour  
			Integer availablePlaces = 0;
			// set it to maximally available places for current tour
			availablePlaces = tour.getMax_member_count();
			log.info("tour.getMax_member_count()" + tour.getMax_member_count());
			// setUsersTourDao(new UsersTourDao());

			// variable stores amount of already registered to the tour users
			int registeredUsers = 0;
			// get all records from data base table UsersTours, 
			// where as a tour is set currently specified tour
			List<UsersTour> usersTours = usersTourDao.getUsersTourByTour(tour);
			// when list is not null, then amount of list elements is the amount
			// of registered to the specified tour users
			if (!usersTours.equals(null)) {
				registeredUsers = usersTours.size();
			}
			
			// when places are more then already registered users, 
			// set to flag possibleToRegister value 1,
			// else set to flag value 2
			if (availablePlaces > registeredUsers){
				possibleToRegister = 1;
			} else {
				possibleToRegister = 2;
			}
								
			// user is on the page touroperations
			return "touroperations";
		}

		// get id's from tour and user objects
		Integer userId = user.getId();
		Integer tourId = tour.getId();

		System.out.println("Tour ID " + tourId);

		// set fields for record UsersTour database table record
		usersTour.setUserId(userId);
		usersTour.setTourId(tourId);
		log.info("USER: " + userId);
		log.info("TOUR: " + tourId);

		// save new record to the data base table UsersTour
		usersTourDao.save(usersTour);
		// user is redirected to the page touroperations for specified tour
		return "redirect:touroperations.html?id=" + tour.getId();
	}
	
	/**
	 * In case of Requesting "/unregisterfromtour.html" & RequestMethod = POST,
	 * the Controller opens the .jsp-view touroperations and manages data access
	 * @return touroperations.
	 *  */
	@RequestMapping(value = "/unregisterfromtour.html", method = RequestMethod.POST)
	public String unregisterFromTour(Tour tour, BindingResult result, ModelMap model) {
		// get username of currently logged in user
		Authentication auth = SecurityContextHolder.getContext()
				.getAuthentication();
		String username = auth.getName();
		log.info("NAME: " + username);
		// find user record by specified username
		User user = userDao.findByUsername(username);
		log.info("NAME: " + user.getUsername());
		
		// when result has errors, then no further registration activities
		// are not possible
		// and user is redirected to the touroperations page without any specified tour
		if (result.hasErrors()) {
			log.info("registration to tour failed");
			for (ObjectError error : result.getAllErrors()) {
				log.info(" - error:" + error.toString());
			}
			return "touroperations";
		}

		// get id of specified tour and user
		Integer userId = user.getId();
		Integer tourId = tour.getId();

		// test printout
		// System.out.println("User id from user" + userId);
		// System.out.println("Username from user" + auth.getName()); 
		// System.out.println("Username from user" + user.getUsername());
		// System.out.println("Tour ID " + tourId);

	
		log.info("USER: " + userId);
		log.info("TOUR: " + tourId);

		// search UsersTour record by tour and user
		UsersTour usersTour = usersTourDao.searchByUserAndTour(tour, user);
		if(usersTour == null){
			log.info("sfgdsfhdfghdfghdfghfg NULL" );
		}
		System.out.println("ID: " + usersTour.getUserTourId());
		System.out.println("Tour ID: " + usersTour.getTourId());
		System.out.println("User ID: " + usersTour.getUserId());
		
		try {
			// delete previously found UsersTour record (unregister user from tour)
			usersTourDao.delete(usersTour);
		} catch (DaoException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// redirect user to the touroperations with the specified tour
		return "redirect:touroperations.html?id=" + tour.getId();
	}
	/**
	 * In case of Requesting "/searchtour.html" & RequestMethod = POST,
	 * the Controller opens the .jsp-view tourlist and manages data access
	 * @return tourlist
	 *  */
	@RequestMapping(value = "/searchtour.html", method = RequestMethod.POST)
	public String showTourSearchResultsForm(ModelMap model) {
		List<Tour> actions = null;
		model.addAttribute("tour", new Tour());
		model.addAttribute("actionValues", actions);
		return "tourlist";
	}

}
